﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CoreEx.Common.Data;
using CoreEx.Common.Extensions;
using LinFu.IoC.Interfaces;
using LinFu.IoC;
namespace CoreEx.MicroMapper.Implementation
{
    /// <summary>
    /// A mapper that maps data from an <see cref="ITable"/> to one or more <typeparamref name="TTarget"/> instances.
    /// </summary>
    /// <typeparam name="TTarget">The type of object to map <see cref="IRow"/> instances.</typeparam>
    public class TableMapper<TTarget> : MicroMapper<IRow,TTarget>, ITableMapper<TTarget>
    {

        /// <summary>
        /// Determines if the values on the target instance will set by using a contructor.
        /// </summary>
        /// <returns></returns>
        private static bool ShouldDoConstructorBasedMapping()
        {
            if (typeof(TTarget).IsInterface)
                return false;
            if (typeof(TTarget).IsSystemType() && !string.IsNullOrEmpty(typeof(TTarget).Namespace))
                return false;
            var hasParameterlessConstructor = typeof(TTarget).GetConstructors().Where(c => c.GetParameters().Count() == 0).Count() > 0;
            return !hasParameterlessConstructor;
        }


        private object[] CreateConstructorArguments(IRow row)
        {
            var parameters = typeof (TTarget).GetConstructors().First().GetParameters();
            
            var values = new object[parameters.Length];
            
            for (int i = 0; i < parameters.Length; i++)
            {
                var parameter = parameters[i];
                if (row.Cells.ContainsKey(parameter.Name))
                    values[i] = ValueConverter.Convert(row[parameter.Name], row.Cells[parameter.Name].Column.DataType,
                                                       parameter.ParameterType);
                else
                    values[i] = parameter.ParameterType.GetDefaultValue();

            }
            return values;
        }
        
        
        /// <summary>
        /// Called by the <see cref="IServiceContainer"/> after the <see cref="ITableMapper{TTarget}"/> 
        /// instance has been created.
        /// </summary>
        /// <param name="source">The <see cref="IServiceContainer"/> that created the instance.</param>
        public override void Initialize(IServiceContainer source)
        {
            base.Initialize(source);

            if (typeof(TTarget).IsSystemType() && !string.IsNullOrEmpty(typeof(TTarget).Namespace))
            {
                TargetAccessor = null;
                TargetFactory = (s) =>
                                    {
                                        var column = s.Table.Columns.First().Value;
                                        return
                                            (TTarget)
                                            ValueConverter.Convert(s.Cells.First().Value.Value, column.DataType,
                                                                   typeof (TTarget));
                                    };
            }


            if (ShouldDoConstructorBasedMapping())
            {
                TargetAccessor = null;
                TargetFactory = (s) => (TTarget)source.AutoCreate(typeof (TTarget), CreateConstructorArguments(s));
            }

            SourceAccessorSelector = (row) => row.Table.Columns.Values.Select
                (c => (IAccessorInfo) new AccessorInfo{Name = c.ColumnName,DataType = c.DataType});

            SourceAccessor = (accessorInfo, row) => row[accessorInfo.Name];


            //Find the nested property types 
            var nestedProperties =
                typeof (TTarget).GetProperties().Where(
                    p => !p.PropertyType.IsSystemType() && !p.PropertyType.IsEnumerable());
           
        }
    }
}
